The ooc Language

2011-10-20

Today I've decided to have a quick look at ooc and see if I can accomplish the eBeats challenge in a day.

First of all, it seems like a nice little language, a pretty frontend for C, with some of the ideals of Nimrod, but with a lot more pragmatism.

I've joined #ooc-lang on freenode to get some help along the way.

To start calculating eBeats, we need the current time in UTC, so let's look at what the documentation says: os/Time. Looking at this, I suddenly realize that I have little to no idea of what the docs say, so I better read through the less terse material first. I notice however that there doesn't seem to be anything related to UTC or GMT on this page, that doesn't look good.

Working on getting anything recognizable from gmtime_r(3) doesn't turn out to be easy, but after 2 hours I've finally done it and see some actual times:

include time
import os/Time
import lang/Format

// struct tm *gmtime_r(const time_t *timep, struct tm *result);
gmtime_r: extern func(TimeT*, TMStruct*) -> TMStruct*

main: func () {
  clock : TimeT
  time(clock&)

  time : TMStruct
  gmtime_r(clock&, time&)

  "%02i:%02i:%02i" cformat(time tm_hour, time tm_min, time tm_sec) println()
}

OK, so far everything seems fine, it's nothing to be proud of, but I won't stop here.

After some more fiddling, I come up with a timeToBeats function that takes the time struct and spits out the integer value of eBeats, but I seemingly cannot get anything more fine-grained. Let's see if you can find the bug.

timeToBeats: func (time: TMStruct) -> Float {
  beats := ((time tm_hour) * (1000.0 / 24.0)) +
           ((time tm_min)  * (1000.0 / (24.0 * 60.0))) +
           ((time tm_sec)  * (1000.0 / (24.0 * 60.0 * 60.0)))

  return beats
}

Done looking?

Well, anytime a developer uses Floats, everything has to be checked more than twice. In my case, after a little bit of further investigation and sprinkling cformat/println all over the place, I found that beats wasn't a Float, it was an Int, and ooc was so kind to just let me return an Int from a function that has Float as explicit return type. What it did, and what I cannot understand in modern language design, is that it implicitly converted the Int into a Float, no warnings given, no questions asked.

Let's have a closer look at what's going on there, with a little experiment.

To keep it simple, I just divide a Float by an Int.

10.0 / 5 // 2.000000

The compiler says, when asked for really verbose output:

Resolving variable decl uniqueVar : <unknown type> = 10.0 / 5

For uniqueVar : Float = 10.0 / 5, resolving type Float, of type BaseType

So we really got a Float as result, wonderful!

And now we divide an Int by a Float

10 / 5.0 // 0.000000

The compiler says, when asked for really verbose output:

Resolving variable decl uniqueVar : <unknown type> = 10 / 5.0

For uniqueVar : SSizeT = 10 / 5.0, resolving type SSizeT, of type BaseType

Well, whatever SSizeT is, it doesn't talk or walk like a Float, and the result is way worse than I had expected.

I'm running out of time to finish the post today, so I'll continue with the eBeats instead of diving further into the typing peculiarities of OCC.

After telling it that my return value really, really, is a Float, it complied and gave me the desired result. Here is the finished source:

include time
import os/Time

// struct tm *gmtime_r(const time_t *timep, struct tm *result);
gmtime_r: extern func(TimeT*, TMStruct*) -> TMStruct*

main: func () {
  clock : TimeT
  time(clock&)

  time : TMStruct
  gmtime_r(clock&, time&)

  "@%f" cformat(timeToBeats(time)) println()
}

timeToBeats: func (time: TMStruct) -> Float {
  beats : Float
  beats = ((time tm_hour) * (1000.0 / 24.0)) +
          ((time tm_min)  * (1000.0 / (24.0 * 60.0))) +
          ((time tm_sec)  * (1000.0 / (24.0 * 60.0 * 60.0)))

  return beats
}

Future updates will go directly into the git repo. I of course welcome any contributions.